import { Control } from "ol/control";
import { listen } from "ol/events.js";

/**
 * @typedef {Object} Options
 * @property {function(import("../MapEvent.js").default):void} [render] Function called when the
 * control should be re-rendered. This is called in a `requestAnimationFrame`callback.
 * @property {import("../coordinate.js").CoordinateFormat} [coordinateFormat] Coordinate format.
 * @property {import("../proj.js").ProjectionLike} [projection] Projection. Default is the view projection.
 * @property {string} [className='ol-custom-compass'] CSS class name.
 * @property {HTMLElement|string} [target] Specify a target if you want the
 * control to be rendered outside of the map's viewport.
 * @property {number} [angleDelta=Math.PI/2] Angle delta.
 */

class Compass extends Control {
  constructor(options = {}) {
    const element = document.createElement("div");
    element.className = options.className || "ol-custom-compass";

    element.innerHTML = `
        <button class="left"></button>
        <button class="center"></button>
        <button class="right"></button>
    `;

    super({
      element: element,
      render: options.render,
      target: options.target,
    });

    element
      .querySelector(".left")
      .addEventListener("click", this.reverseClick.bind(this));
    element
      .querySelector(".center")
      .addEventListener("click", this.recoveryClick.bind(this));
    element
      .querySelector(".right")
      .addEventListener("click", this.forwardClick.bind(this));

    this.element_ = element;

    this.angleDelta = options.angleDelta || Math.PI / 2;

    /**
     * 当前罗盘指针的旋转角度
     * @private
     */
    this.transformRotation = 0;

    /**
     * 是否使用handleRotationChange事件
     * @private
     */
    this.isUseHandleRotationChange = true;
  }

  /**
   * @override
   */
  setMap(map) {
    super.setMap(map);
    if (map) {
      this.listenerKeys.push(
        listen(map, "moveend", this.handleRotationChange, this)
      );
    }
  }

  /**
   * @private
   */
  handleRotationChange(event) {
    const currentRotation =
      (this.getMap()?.getView().getRotation() * 180) / Math.PI;

    if (this.isUseHandleRotationChange) {
      this.setPointerRotation(currentRotation);
    }

    this.isUseHandleRotationChange = true;
  }

  /**
   * 设置指针旋转
   * @param {number} rotation 旋转角度
   * @param {boolean} isUseTransition 是否使用过渡
   */
  setPointerRotation(rotation, isUseTransition = true) {
    const pointer = this.element_.querySelector(".center");

    pointer.style.transform = `rotate(${rotation}deg)`;
    pointer.style.transition = isUseTransition ? "all 0.5s" : "none";

    this.transformRotation = rotation;
  }

  /**
   * 模仿view.animate的格式化旋转角度，将旋转角度限制在-180到180之间
   * @param {number} rotation 旋转角度
   * @returns {number} 格式化后的旋转角度
   */
  formatRotation(rotation) {
    if (rotation < 180 && rotation >= -180) {
      return rotation;
    } else {
      return this.formatRotation(
        rotation > 0 ? rotation - 360 : rotation + 360
      );
    }
  }

  /**
   * 逆时针旋转
   * @private
   */
  reverseClick() {
    const view = this.getMap()?.getView();

    if (view) {
      this.isUseHandleRotationChange = false;

      const center = view.getCenter();
      const rotation = view.getRotation();

      view.animate({
        center,
        duration: 200,
        rotation: rotation - this.angleDelta,
      });

      this.setPointerRotation(
        this.transformRotation - (this.angleDelta * 180) / Math.PI
      );
    }
  }

  /**
   * 顺时针旋转
   * @private
   */
  forwardClick() {
    const view = this.getMap()?.getView();

    if (view) {
      this.isUseHandleRotationChange = false;

      const center = view.getCenter();
      const rotation = view.getRotation();

      view.animate({
        center,
        duration: 200,
        rotation: rotation + this.angleDelta,
      });

      this.setPointerRotation(
        this.transformRotation + (this.angleDelta * 180) / Math.PI
      );
    }
  }

  /**
   * 恢复到0度
   * @private
   */
  recoveryClick() {
    const view = this.getMap()?.getView();

    if (view) {
      this.isUseHandleRotationChange = false;

      const center = view.getCenter();
      view.animate({
        center,
        duration: 200,
        rotation: 0,
      });

      this.setPointerRotation(
        this.formatRotation(this.transformRotation),
        false
      );

      //因为上一次的动画没有结束，所以需要等待动画结束后再设置为0
      requestAnimationFrame(() => {
        this.setPointerRotation(0);
      });
    }
  }
}

export default Compass;
